/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.shape.impl;

import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;

import who.tmap.common.TMapException;
import who.tmap.toolkit.shape.MapShapes;
import who.tmap.toolkit.shape.PolygonShape;
import who.tmap.toolkit.shape.annotation.LineShape;

/**
 * TMap project
 * WHO 2004-2005 
 * A helper to add a common boundary (a set of points) 
 * from an existing polygon to another polygon
 * 
 * @author stl
 */
public class PolygonShapeImplCommonBoundaryHelper extends BasePolygonShapeHelper {

    private static Logger logger = Logger.getLogger(PolygonShapeImplCommonBoundaryHelper.class);      
    
    private List<PolygonShapePoint> cbpPoints =null; 
    private Point2D firstPt =null, secondPt =null, pathPt =null;
    private PolygonShape shape =null, cbp =null;
    private double mapFactor, zoomFactor;
    
    public PolygonShapeImplCommonBoundaryHelper( PolygonShape shape, PolygonShape cbp, 
            									 Point2D firstPt, Point2D secondPt, 
            									 Point2D pathPt, 
            									 double mapFactor, double zoomFactor ) {
        this.shape =shape;
        this.cbp =cbp;
        this.firstPt =firstPt;
        this.secondPt =secondPt;
        this.pathPt =pathPt;
        this.mapFactor =mapFactor;
        this.zoomFactor =zoomFactor;
    }
    
    @SuppressWarnings("unchecked")
	public void addCommonBoundary() throws TMapException {
        try {
            cbpPoints =generalPathToArray( cbp.getGeneralPath() );
            
            PolygonShapePoint firstPsp =findPoint( cbpPoints, firstPt );
            PolygonShapePoint secondPsp =findPoint( cbpPoints, secondPt );

            logger.debug( "firstPsp=(" + firstPsp.x + ", " + firstPsp.y + ")\nsecondPsp=(" + secondPsp.x + ", " + secondPsp.y + ")" );
            
            // Make sure the two points belong to the same shape
            if ( firstPsp.polygonId != secondPsp.polygonId ) {
                throw new TMapException( "The two points do not belong to the same polygon!");
            }
            
            // to make things easier, we only keep the polygon containing the 2 points
            cbpPoints =extractPointsForPolygonIdOnly( firstPsp.polygonId, (ArrayList<PolygonShapePoint>)cbpPoints );
             
            boolean success =false;
            CircularListIterator cli =new CircularListIterator( (ArrayList)cbpPoints );
            
            int iterationCt =0;
            while( ++iterationCt <= 2 ) {

                logger.debug( "iterationCt=" + iterationCt );
                
                GeneralPath path =new GeneralPath(); 
                boolean foundPathPt =false;  
                
	            cli.iterateUntil( firstPsp );
	            
	            PolygonShapePoint prevPsp =(PolygonShapePoint)firstPsp.clone();
	
	            while ( !cli.hasTraversedAllElements() ) {
	                PolygonShapePoint psp =(PolygonShapePoint)cli.iterate(); 
	                
	                if ( path.getCurrentPoint() != null ) {
	                    path.lineTo( psp.x, psp.y );
	                } else {
	                    path.moveTo( psp.x, psp.y );
	                }
	                
	                if ( lookForPathPoint( prevPsp, psp, pathPt ) ) {
	                    logger.debug( "Found the path point!" );
	                    foundPathPt =true;
	                }
	                
	                if ( psp.equals( secondPsp ) ) {
	                    logger.debug( "Found the 2nd point!");
	                    break;
	                }
	                
	                prevPsp =(PolygonShapePoint)psp.clone();
	            } // while 
	            
	            if ( foundPathPt ) {
	                shape.getGeneralPath().append( path.getPathIterator(null), true );
	                success =true;
	                logger.debug( "addCommonBoundary success! iterationCt=" + iterationCt );
	                break;
	            } else {
	                cli.reverseDirection();
	            }

            }
            
            if (!success) {
                logger.debug( "addCommonBoundary failed :-(");
            }
            
        } catch (TMapException e) {
            e.printStackTrace();
        }
    }
    
    private boolean lookForPathPoint( PolygonShapePoint prevPsp, PolygonShapePoint psp, Point2D pathPt ) throws TMapException {
        LineShape line =(LineShape)MapShapeFactory.getInstance().getGraphic( MapShapes.LINE );
        line.setMapFactor( mapFactor );
        line.setZoomFactor( zoomFactor );
        
        line.setP1( new Point2D.Double( prevPsp.x, prevPsp.y ) );
        line.setP2( new Point2D.Double( psp.x, psp.y) );
        
        return line.contains( pathPt );
    }

    
}
